﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace UI
{
    using Common.Protocol;

    public partial class ChartCanvas
    {
        #region private fields

        private Chart parent;

        private Color gridLinesColor, datesColor;
        //private Font datesFont, currentDateFont;
        private int horizontalIndent, verticalIndent, redLinePosition;

        Typeface typeface = new Typeface(new FontFamily("calibri"), FontStyles.Normal, new FontWeight(), new FontStretch());
        CultureInfo cultureInfo = CultureInfo.CurrentUICulture;
        
        #endregion

        private List<ProtocolInterval> data;

        private long totalMilliseconds;

        public ChartCanvas(Chart container)
        {
            this.InitializeComponent();
            this.SnapsToDevicePixels = true;

            this.datesColor = Colors.Gray;
            this.gridLinesColor = Colors.Gainsboro;
            
            this.horizontalIndent = 42;
            this.verticalIndent = 30;
            this.redLinePosition = (int)this.Height / 2;

            this.parent = container;

            this.data = null;

            this.ChannelValues = new List<List<int>>();
        }

        #region public propertis

        public int VerticalIndent
        {
            get
            {
                return this.verticalIndent;
            }
            set
            {
                this.verticalIndent = value;
                this.InvalidateVisual();
            }
        }

        public int HorizontalIndent
        {
            get
            {
                return this.horizontalIndent;
            }
            set
            {
                this.horizontalIndent = value;
                this.InvalidateVisual();
            }
        }

        public Color GridLinesColor
        {
            get
            {
                return this.gridLinesColor;
            }
            set
            {
                this.gridLinesColor = value;
                this.InvalidateVisual();
            }
        }

        public Color DatesColor
        {
            get
            {
                return this.datesColor;
            }
            set
            {
                this.datesColor = value;
                this.InvalidateVisual();
            }
        }

        public List<List<int>> ChannelValues { get; private set; }
        
        #endregion

        #region drawing functions

        protected override void OnRender(DrawingContext drawingContext)
        {
            this.canvas.Children.Clear();

            this.DrawDates();
            this.DrawGrid(drawingContext);
            this.DrawCharts(drawingContext);
            this.DrawRedLine(drawingContext);
        }

        private void DrawDates()
        {
            string date, time;
            int x, y = 0;

            //calculating time shift between horizontals
            var ticks = (this.parent.EndTime - this.parent.StartTime).TotalMilliseconds;
            int intervalsCount = 0;
            while (y < this.RenderSize.Height)
            {
                intervalsCount++;
                y += this.VerticalIndent;
            }

            intervalsCount--;
            intervalsCount--;
            int timeShift = (int)(ticks / intervalsCount);

            Brush brush = new SolidColorBrush(this.DatesColor);

            // drawing dates
            y = 0;
            DateTime toDraw = this.parent.StartTime;
            while (y < this.RenderSize.Height)
            {
                if (y < this.redLinePosition - this.verticalIndent / 2 || y > this.redLinePosition + this.verticalIndent / 2)
                {
                    // time printing
                    time = toDraw.ToLongTimeString();
                    TextBlock timeTextBlock = new TextBlock();
                    timeTextBlock.Text = time;
                    timeTextBlock.FontSize = 12;
                    timeTextBlock.Foreground = brush;
                    timeTextBlock.Margin = new Thickness(0, y, 0, 0);
                    this.canvas.Children.Add(timeTextBlock);
                    
                    // date printing
                    date = toDraw.ToShortDateString();
                    TextBlock dateTextBlock = new TextBlock();
                    dateTextBlock.Text = date;
                    dateTextBlock.FontSize = 12;
                    dateTextBlock.Foreground = brush;
                    x = (int)(this.RenderSize.Width - 65);
                    dateTextBlock.Margin = new Thickness(x, y, 0, 0);
                    dateTextBlock.HorizontalAlignment = HorizontalAlignment.Right;
                    this.canvas.Children.Add(dateTextBlock);
                }

                y += this.VerticalIndent;
                toDraw = toDraw.AddMilliseconds(timeShift);
            }
        }

        private void DrawCharts(DrawingContext drawingContext)
        {
            #region old version
            //if (this.data != null)
            //{
            //    foreach (var channelInterval in this.data)
            //    {
            //        Point point = new Point();
            //        point.Y = 0;

            //        Polyline line = new Polyline();
            //        Color color = new Color();
            //        color.A = channelInterval.Config.Color.A;
            //        color.R = channelInterval.Config.Color.R;
            //        color.G = channelInterval.Config.Color.G;
            //        color.B = channelInterval.Config.Color.B;
            //        line.Stroke = new SolidColorBrush(color);
            //        line.StrokeThickness = 3;

            //        double periodVerticalShift = this.RenderSize.Height / channelInterval.Values.Count;

            //        foreach (float val in channelInterval.Values)
            //        {
            //            point.Y += periodVerticalShift;
            //            point.X = (val / channelInterval.Config.ValueMax) * this.RenderSize.Width;
            //            line.Points.Add(point);
            //        }

            //        line.VerticalAlignment = VerticalAlignment.Stretch;

            //        Polyline lineToAdd = new Polyline();
            //        lineToAdd.Stroke = line.Stroke;
            //        lineToAdd.StrokeThickness = 1;
            //        lineToAdd.VerticalAlignment = VerticalAlignment.Stretch;
            //        lineToAdd.Points = line.Points.Clone();

            //        this.canvas.Children.Add(lineToAdd);
            //        break;
            //    }
            //}
            #endregion

            if (this.data != null)
            {
                double y = 0;

                foreach (var interval in this.data)
                {
                    double intervalHeight =
                        ((interval.ChannelIntervals[0].End - interval.ChannelIntervals[0].Start).TotalMilliseconds
                        / (double)this.totalMilliseconds) * this.RenderSize.Height;

                    foreach (var channelInterval in interval.ChannelIntervals)
                    {
                        if (channelInterval.IsActive)
                        {
                            Point point = new Point();
                            point.Y = y;

                            Polyline line = new Polyline();
                            Color color = new Color();
                            color.A = channelInterval.Config.Color.A;
                            color.R = channelInterval.Config.Color.R;
                            color.G = channelInterval.Config.Color.G;
                            color.B = channelInterval.Config.Color.B;
                            line.Stroke = new SolidColorBrush(color);
                            line.StrokeThickness = 2;

                            double intervalShift = 0;
                            double periodVerticalShift = intervalHeight / channelInterval.Values.Count;

                            point.Y -= periodVerticalShift;

                            foreach (float val in channelInterval.Values)
                            {
                                intervalShift += periodVerticalShift;
                                point.Y += periodVerticalShift;
                                point.X = (val / channelInterval.Config.ValueMax) * this.RenderSize.Width;
                                line.Points.Add(point);
                            }

                            this.canvas.Children.Add(line);
                        }
                    }

                    y += intervalHeight;
                    this.DrawGrayLine(drawingContext, y);
                }
            }
        }

        private void DrawGrayLine(DrawingContext drawingContext, double y)
        {
            drawingContext.DrawLine(new Pen(Brushes.DarkSlateGray, 2), new Point(0, y), new Point(this.RenderSize.Width, y));
        }

        private void DrawGrid(DrawingContext drawingContext)
        {
            int x = 0, y = this.VerticalIndent;
            Brush b = new SolidColorBrush(this.GridLinesColor);
            Pen p = new Pen(b, 1);

            // drawing horizontals
            while (y < this.RenderSize.Height)
            {
                Point start = new Point(x, y);
                Point end = new Point(this.RenderSize.Width - x - 1, y);
                drawingContext.DrawLine(p, start, end);
                y += this.VerticalIndent;
            }

            // drawing verticals
            while (x < this.RenderSize.Width)
            {
                Point start = new Point(x, 0);
                Point end = new Point(x, this.RenderSize.Height);
                drawingContext.DrawLine(p, start, end);
                x += this.HorizontalIndent;
            }
        }

        private void DrawRedLine(DrawingContext drawingContext)
        {
            string date, time;

            date = this.parent.CurrentTime.ToShortDateString();
            time = this.parent.CurrentTime.ToLongTimeString();

            this.redLinePosition = (int)this.RenderSize.Height / 2;

            FormattedText text = new FormattedText(time, cultureInfo, FlowDirection.LeftToRight, typeface, 14, Brushes.Red);
            drawingContext.DrawText(text, new Point(2, this.redLinePosition));

            text = new FormattedText(date, cultureInfo, FlowDirection.LeftToRight, typeface, 14, Brushes.Red);
            drawingContext.DrawText(text, new Point(this.RenderSize.Width - text.Width - 2, this.redLinePosition));

            drawingContext.DrawLine(new Pen(Brushes.Red, 1), new Point(0, this.redLinePosition), new Point(this.RenderSize.Width, this.redLinePosition));
        }

        #endregion

        private void SizeChangedEventHandler(object sender, SizeChangedEventArgs e)
        {
            this.redLinePosition = (int)this.RenderSize.Height / 2;
            this.InvalidateVisual();
        }

        public void SetChannelData(List<ProtocolInterval> data, long totalMilliseconds)
        {
            this.totalMilliseconds = totalMilliseconds;
            this.data = data;
            this.InvalidateVisual();
        }
    }
}